﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Reflection;

namespace Framework.Common
{
    public class ObjectDumperHelper
    {
        #region output
        /// <summary>
        /// Console Write
        /// </summary>
        /// <param name="obj"></param>
        public static void WriteToConsole(object obj)
        {
            Console.WriteLine(Write(obj, 4));
        }

        /// <summary>
        /// Console Write
        /// </summary>
        /// <param name="obj"></param>
        public static void WriteToLog(object obj)
        {
            LogHelper.Debug(Write(obj, 4));
        }

        #endregion

        #region get StringBuilder
        /// <summary>
        /// get StringBuilder from object with depth zero
        /// </summary>
        /// <param name="o"></param>
        /// <returns></returns>
        public static StringBuilder Write(object o)
        {
            return Write(o, 0);
        }

        /// <summary>
        /// get StringBuilder from object
        /// </summary>
        /// <param name="o"></param>
        /// <param name="depth"></param>
        /// <returns></returns>
        public static StringBuilder Write(object o, int depth)
        {
            ObjectDumperHelper dumper = new ObjectDumperHelper(depth);
            dumper.Write(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
            dumper.Write("TYPE IS " + o.GetType().FullName);
            dumper.WriteObject(null, o);
            dumper.Write("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
            return dumper.stringBuilder;
        }
        #endregion

        #region core
        StringBuilder stringBuilder;
        int pos;
        int level;
        int depth;

        private ObjectDumperHelper(int depth)
        {
            this.stringBuilder = new StringBuilder();
            this.depth = depth;
        }

        private void Write(string s)
        {
            if (s != null)
            {
                stringBuilder.Append(s);
                pos += s.Length;
            }
        }

        private void WriteIndent()
        {
            stringBuilder.Append(new string('\t', level));
        }

        private void WriteLine()
        {
            stringBuilder.AppendLine();
            pos = 0;
        }

        private void WriteTab()
        {
            Write("  ");
            while (pos % 8 != 0) Write(" ");
        }

        private void WriteObject(string prefix, object o)
        {
            if (o == null || o is ValueType || o is string)
            {
                WriteIndent();
                Write(prefix);
                WriteValue(o);
                WriteLine();
            }
            else if (o is IEnumerable)
            {
                foreach (object element in (IEnumerable)o)
                {
                    if (element is IEnumerable && !(element is string))
                    {
                        WriteIndent();
                        Write(prefix);
                        Write("...");
                        WriteLine();
                        if (level < depth)
                        {
                            level++;
                            WriteObject(prefix, element);
                            level--;
                        }
                    }
                    else
                    {
                        WriteObject(prefix, element);
                    }
                }
            }
            else
            {
                MemberInfo[] members = o.GetType().GetMembers(BindingFlags.Public | BindingFlags.Instance);
                WriteIndent();
                Write(prefix);
                bool propWritten = false;
                foreach (MemberInfo m in members)
                {
                    FieldInfo f = m as FieldInfo;
                    PropertyInfo p = m as PropertyInfo;
                    if (f != null || p != null)
                    {
                        if (!propWritten)
                        {
                            propWritten = true;
                        }

                        WriteLine();
                        Write(new String('\t', level));
                        Write(m.Name);
                        Write("=");
                        Type t = f != null ? f.FieldType : p.PropertyType;
                        if (t.IsValueType || t == typeof(string))
                        {
                            WriteValue(f != null ? f.GetValue(o) : p.GetValue(o, null));
                        }
                        else
                        {
                            if (typeof(IEnumerable).IsAssignableFrom(t))
                            {
                                Write("...");
                            }
                            else
                            {
                                Write("{ }");
                            }
                        }
                    }
                }
                if (propWritten) WriteLine();
                if (level < depth)
                {
                    foreach (MemberInfo m in members)
                    {
                        FieldInfo f = m as FieldInfo;
                        PropertyInfo p = m as PropertyInfo;
                        if (f != null || p != null)
                        {
                            Type t = f != null ? f.FieldType : p.PropertyType;
                            if (!(t.IsValueType || t == typeof(string)))
                            {
                                object value = f != null ? f.GetValue(o) : p.GetValue(o, null);
                                if (value != null)
                                {
                                    level++;
                                    WriteObject(m.Name + ": ", value);
                                    level--;
                                }
                            }
                        }
                    }
                }
            }
        }

        private void WriteValue(object o)
        {
            if (o == null)
            {
                Write("null");
            }
            else if (o is DateTime)
            {
                Write(((DateTime)o).ToShortDateString());
            }
            else if (o is ValueType || o is string)
            {
                Write(o.ToString());
            }
            else if (o is IEnumerable)
            {
                Write("...");
            }
            else
            {
                Write("{ }");
            }
        }
        #endregion
    }

}
